#ifndef _REGULAR_GRID_H_
#define _REGULAR_GRID_H_

#include "../cuCommon/cuTypes.h"
#include "../cuCommon/cuMath.h"
#include "../cuCommon/cuBuffer.h"

#include "Ray.h"

template <class T>
struct TRegularGrid
{
protected :
  TBuffer<T> m_buffer;
  uint32 m_Sx, m_Sy, m_Sz;
  uint32 m_Total;

	//////////////////////////////////////////////////////////////////////////
	/// Null() function sets class members to zero
	//////////////////////////////////////////////////////////////////////////
	void Null()
	{
		m_Sx = m_Sy = m_Sz = m_Total = 0;		
	}

public:

	//////////////////////////////////////////////////////////////////////////
	/// operator (uint) returns buffer CUDA ptr
	//////////////////////////////////////////////////////////////////////////
  T * operator () (uint32 b)
  {
    return this->m_buffer(b);
  }

	//////////////////////////////////////////////////////////////////////////
	/// operator [uint] returns buffer Host ptr
	//////////////////////////////////////////////////////////////////////////
  T * operator [] (uint32 b)
  {
    return this->m_buffer[b];
  }

	//////////////////////////////////////////////////////////////////////////
	/// operator (uint, uint) returns element from host buffer
	//////////////////////////////////////////////////////////////////////////
  T operator () (uint32 elementIdx, uint32 b ) const
  {
    return this->m_buffer[b][elementIdx];
  }

	//////////////////////////////////////////////////////////////////////////
	/// operator (uint, uint) returns element from host buffer
	//////////////////////////////////////////////////////////////////////////
  T & operator () (uint32 elementIdx, uint32 b ) 
  {
    return this->m_buffer[b][elementIdx];
  }

	//////////////////////////////////////////////////////////////////////////
	/// operator (uint, uint, uint, uint) returns element(i,j,k) from host buffer
	//////////////////////////////////////////////////////////////////////////
  T operator () (uint32 i, uint32 j, uint32 k, uint32 b) const
  {
    return this->m_buffer[b][i + m_Sx * (j + m_Sy * k)];
  }

	//////////////////////////////////////////////////////////////////////////
	/// operator (uint, uint, uint, uint) returns element(i,j,k) from host buffer
	//////////////////////////////////////////////////////////////////////////
  T & operator () (uint32 i, uint32 j, uint32 k, uint32 b) 
  {
    return this->m_buffer[b][i + m_Sx * (j + m_Sy * k)];
  }

	//////////////////////////////////////////////////////////////////////////
	/// Ctor
	//////////////////////////////////////////////////////////////////////////
	TRegularGrid()
	{
		Null();
	}

	TRegularGrid(uint32 sx, 
		           uint32 sy, 
							 uint32 sz, 
							 uint8  cFlags = BUFFER_H,
							 bool   bDoubleBuffer = false,	
							 bool   bClear = false, 
							 T     *pInitElem = NULL)
	{
		Create(sx, sy, sz, cFlags, bDoubleBuffer, bClear, pInitElem);
	}

	void Create(uint32 sx, 
		          uint32 sy, 
		          uint32 sz, 
							uint8  cFlags = BUFFER_H,
							bool   bDoubleBuffer = false,
							bool   bClear = false, 
		          T     *pInitElem = NULL)
	{
		m_Sx = sx;
		m_Sy = sy;
		m_Sz = sz;
		m_Total = sx * sy * sz;

		m_buffer.Create(m_Total, cFlags, bDoubleBuffer);

		if (bClear)
		{
			uint8 clearFlags = BUFFER_R | (bDoubleBuffer * BUFFER_W);
			Clear(pInitElem, clearFlags);
		}
	}

	void Release(uint8 rFlags = BUFFER_H)
	{
		m_buffer.Release(rFlags);

		Null();
	}

	T get(uint32 ix, uint32 iy, uint32 iz, uint32 ib = BUFFER_R)
	{
#ifdef _DEBUG
		ix = clamp(ix, (uint32) 0, m_Sx-1);
		iy = clamp(iy, (uint32) 0, m_Sy-1);
		iz = clamp(iz, (uint32) 0, m_Sz-1);
#endif

		return m_buffer[ib][ix + (iy + iz * m_Sy) * m_Sx];
	}

	T set(T t, uint32 ix, uint32 iy, uint32 iz, uint32 ib = BUFFER_R)
	{
#ifdef _DEBUG
		ix = clamp(ix, (uint32) 0, m_Sx-1);
		iy = clamp(iy, (uint32) 0, m_Sy-1);
		iz = clamp(iz, (uint32) 0, m_Sz-1);
#endif

		return m_buffer[ib][ix + (iy + iz * m_Sy) * m_Sx] = t;
	}

	uint3 dim()
	{
		return make_uint3(m_Sx, m_Sy, m_Sz);
	}

	void Clear(T * pInitElem = NULL, uint8 clearFlags = BUFFER_R)
	{
		T zeroval;

		if (pInitElem != NULL)
			zeroval = pInitElem[0];
		else
			memset(&zeroval, 0, sizeof(T));

		m_buffer.Memset(&zeroval, 0, 0, clearFlags);
	}

	void Swap()
	{
		m_buffer.Swap();
	}

	TBuffer<T> * getBufferPtr() { return &m_buffer; }
};

typedef TRegularGrid<float4>   Sf4Grid;
typedef TRegularGrid<float >   Sf_Grid;
typedef TRegularGrid<uchar4>   Suc4Grid;
typedef TRegularGrid<uint32>   Sui_Grid;
typedef TRegularGrid<uint2 >   Sui2Grid;
typedef TRegularGrid<SRay>     SRayGrid;

#endif